-module(subscriber_lte).
-behaviour(gen_server).
-include("nas.hrl").
-include("S1AP.hrl").
-include("protocol.hrl").
-include("subscriber.hrl").
-include("cdr.hrl").
-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3]).
init([Status]) ->
  process_flag(trap_exit, false),
	{ok,{running,Status#sub_lte{status=idle,cdr=#lte_s1_cdr{}}}}.


handle_call({nas_msg,S1Ap_Nas},Pid,{running,Status}) when S1Ap_Nas#s1ap_nas.index =:= 10297 ->
	io:format("handle nas_msg ~p from ~p in Status:~p~n",[S1Ap_Nas#s1ap_nas.index,Pid,Status]),
  NewSta = handle_nas(S1Ap_Nas,Status),
	{reply,ok,{running,NewSta}};

handle_call({nas_msg,S1Ap_Nas},Pid,{running,Status}) when is_record(S1Ap_Nas,s1ap_nas) ->
	NewSta = handle_nas(S1Ap_Nas,Status),
	%%io:format("handle nas_msg ~p from ~p in Status:~p~n",[S1Ap_Nas#s1ap_nas.index,Pid,NewSta#sub_lte.status]),
	{reply,ok,{running,NewSta}};

handle_call({rrc_connect,establish,S1Ap_Nas},_,{running,Status}) when (Status#sub_lte.rrc_connect /= active) and
                                                                      (S1Ap_Nas#s1ap_nas.enb_ue_id =:= 170)->
  {_PlmnId,CellId} = S1Ap_Nas#s1ap_nas.cgi,
  {_,Tac} = S1Ap_Nas#s1ap_nas.tai,
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{start_time=S1Ap_Nas#s1ap_nas.datetime,
                                        aux_info=[{message,S1Ap_Nas#s1ap_nas.index}],
                                        cellid = CellId,
                                        tac = Tac,
                                        access_info={
                                             S1Ap_Nas#s1ap_nas.connection#s1ap_context.mme,
                                             S1Ap_Nas#s1ap_nas.connection#s1ap_context.enodeb
                                        }},
  %%io:format("create sub with ~p in ~p with CDR:~p ~n",[S1Ap_Nas,self(),Cdr]),
  {reply,ok,{running,Status#sub_lte{rrc_connect=active,cdr=Cdr}}};

handle_call({rrc_connect,establish,S1Ap_Nas},_,{running,Status}) when Status#sub_lte.rrc_connect /= active ->
  {_PlmnId,CellId} = S1Ap_Nas#s1ap_nas.cgi,
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{start_time=S1Ap_Nas#s1ap_nas.datetime,
                                        aux_info=[{message,S1Ap_Nas#s1ap_nas.index}],
                                        cellid = CellId,
                                        access_info={
                                             S1Ap_Nas#s1ap_nas.connection#s1ap_context.mme,
                                             S1Ap_Nas#s1ap_nas.connection#s1ap_context.enodeb
                                        }},
	{reply,ok,{running,Status#sub_lte{rrc_connect=active,cdr=Cdr}}};

handle_call({rrc_connect,reset},_,{running,Status}) when Status#sub_lte.rrc_connect /= deactive->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{end_time=calendar:local_time()},
	{reply,ok,{running,Status#sub_lte{rrc_connect=deactive,cdr=end_nas_session(Cdr,force),status = idle}}};

handle_call({rrc_connect,release,S1Ap_Nas},_,{running,Status}) when (S1Ap_Nas#s1ap_nas.res /= undefined) ->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{end_time=S1Ap_Nas#s1ap_nas.datetime,end_reason=S1Ap_Nas#s1ap_nas.res},
  {reply,ok,{running,Status#sub_lte{rrc_connect=deactive,cdr=end_nas_session(Cdr,error),status = idle}}};

handle_call({rrc_connect,release,S1Ap_Nas},_,{running,Status}) when (Status#sub_lte.cdr)#lte_s1_cdr.rrc_con_reason =:=auth_request->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{end_time=S1Ap_Nas#s1ap_nas.datetime,end_reason=S1Ap_Nas#s1ap_nas.res},
  {reply,ok,{running,Status#sub_lte{rrc_connect=deactive,cdr=end_nas_session(Cdr,normal),status = idle}}};

handle_call({rrc_connect,release,S1Ap_Nas},_,{running,Status}) when (S1Ap_Nas#s1ap_nas.enb_ue_id =:= undefined)->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{end_time=S1Ap_Nas#s1ap_nas.datetime},
	io:format("lte_subscriber rrc_release for ~p by ~p with CDR:~p in ~p~n",[Status,S1Ap_Nas,Cdr,self()]),
	{reply,ok,{running,Status#sub_lte{rrc_connect=deactive,cdr=end_nas_session(Cdr,normal),status = idle}}};

handle_call({rrc_connect,release,S1Ap_Nas},_,{running,Status}) ->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{end_time=S1Ap_Nas#s1ap_nas.datetime},
	{reply,ok,{running,Status#sub_lte{rrc_connect=deactive,cdr=end_nas_session(Cdr,normal),status = idle}}};

handle_call(dump,_,State) ->
	{reply,{ok,State},State};

handle_call(_,_,State) ->
	{reply, {error,"no func define for subscriber_lte"}, State}.

handle_cast(Request, State) ->
	error_logger:format("unkown request received~p~n",[Request]),
	{noreply,State}.
handle_info(_, State) ->
	{noreply,State}.
terminate(_, _) ->
	ok.
code_change(_, State, _) ->
	{ok,State}.

handle_s1ap(S1Ap_Nas,Status) when (Status#sub_lte.status =:= service_request) and (S1Ap_Nas#s1ap_nas.s1ap_proc =:= ?'id-InitialContextSetup') ->
	Cdr = Status#sub_lte.cdr,
	NewRab = lists:append(Cdr#lte_s1_cdr.rab_info,S1Ap_Nas#s1ap_nas.rab_info),%%{rab_info=S1Ap_Nas#s1ap_nas.rab_info},
  Rate = case S1Ap_Nas#s1ap_nas.rate of
         {0,0} ->
             Cdr#lte_s1_cdr.rate;
         _ ->
             S1Ap_Nas#s1ap_nas.rate
         end,
  Status#sub_lte{step = 2,cdr=Cdr#lte_s1_cdr{rab_info = NewRab,rate=Rate}};

handle_s1ap(#s1ap_nas{index=Index,s1ap_proc=Proc},Status) ->
	%%io:format("unknown s1ap message:~p with Proc:~p at ~p ~n",[Index,Proc,Status]),
  Status.

handle_nas(S1Ap_Nas,Status) when is_record(S1Ap_Nas,s1ap_nas) and (S1Ap_Nas#s1ap_nas.nas =:= <<>>) ->
	handle_s1ap(S1Ap_Nas,Status);

handle_nas(S1Ap_Nas,Status) when is_record(S1Ap_Nas,s1ap_nas) and (S1Ap_Nas#s1ap_nas.nas =/= <<>>) ->
	case S1Ap_Nas#s1ap_nas.nas of
	<<16#c7,Other/binary>> ->
		handle_nas({?EPS_Service_Request,<<0>>,Other},S1Ap_Nas,Status); %% no option in message

	<<16#47,_AuthCode:32,_Seq,Others/bitstring>> -> %%with integrity and ciphered with new EPS security context
		%% the message is ciphered, ignore it
		Status;

	<<16#37,_AuthCode:32,_Seq,Others/bitstring>> -> %%Integrity protect with new EPS sec context
		<<0:4/integer,_Proto:4/integer,Msg_Type:8/integer,IEs/binary>> = Others,
    NewState = handle_nas({Msg_Type,IEs},S1Ap_Nas,Status);

	<<16#27,_AuthCode:32,_Seq,Others/bitstring>> -> %%with integrity and ciphered
		%% the message is ciphered, ignore it
    Status;

	<<16#17,_AuthCode:32/integer,_Seq:8/integer,Others/bitstring>> -> %%with integrity protection
		<<0:4/integer,_Proto:4/integer,Msg_Type:8/integer,Option:8/integer,IEs/binary>> = Others,
    NewState = handle_nas({Msg_Type,Option,IEs},S1Ap_Nas,Status);

  <<16#07,MsgType:8/integer,Others:8/integer>> ->
    NewState = handle_nas({MsgType,<<0>>,Others},S1Ap_Nas,Status);

  <<16#07,MsgType:8/integer,Others/binary>> -> %%with integrity protection
    <<Options:1/binary,IEs/binary>> = Others,
		NewState = handle_nas({MsgType,Options,IEs},S1Ap_Nas,Status);
	_ ->
		io:format("unknow nas in ~p ~n",[S1Ap_Nas]),
		Status
	end.

handle_nas({?MM_TA_Update_Request,Option,IEs},S1Ap_Nas,Status) ->
  {IEs1,NewStatus1} = nas_element(guti,IEs,Status),
	Cdr = (NewStatus1#sub_lte.cdr)#lte_s1_cdr{rrc_con_reason=tac,
                                            user_id=user_id_extract(NewStatus1)},
  case S1Ap_Nas#s1ap_nas.index of
  0 ->
    io:format("after extra nas and get tac ~p with ~p~n",[NewStatus1,IEs]);
  _ ->
    ok
  end,
	NewStatus1#sub_lte{status = tac,step = 1,cdr=Cdr};

handle_nas({?EPS_Service_Request,_Option,_IEs},S1Ap_Nas,Status)  ->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{rrc_con_reason=service_request,
                                        user_id=user_id_extract(Status)
                                         },
 	Status#sub_lte{status = service_request,step = 1,cdr=Cdr};

handle_nas({?EPS_Service_Reject,_Option,_IEs},S1Ap_Nas,Status) ->
  Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{end_reason=service_reject},
  Status#sub_lte{cdr=Cdr};
handle_nas({?MM_Sec_Mod_Cmd,IEs},S1Ap_Nas,Status)  ->
  Status;

handle_nas({?MM_Attach_Request,Option,IEs},S1Ap_Nas,Status) when S1Ap_Nas#s1ap_nas.index =:= 10297 ->
  io:format("handle attache~n"),
  {Other1,NewStatus1} = nas_element(guti,IEs,Status),
	Cdr = (NewStatus1#sub_lte.cdr)#lte_s1_cdr{rrc_con_reason=attach_request,user_id=user_id_extract(NewStatus1)},
  NewStatus1#sub_lte{status = attach_request,step = 1,cdr=Cdr};

handle_nas({?MM_Attach_Request,Option,IEs},S1Ap_Nas,Status) ->
  {Other1,NewStatus1} = nas_element(guti,IEs,Status),
	Cdr = (NewStatus1#sub_lte.cdr)#lte_s1_cdr{rrc_con_reason=attach_request,user_id=user_id_extract(NewStatus1)},
  NewStatus1#sub_lte{status = attach_request,step = 1,cdr=Cdr};

handle_nas({?MM_Auth_Request,Option,IEs},S1Ap_Nas,Status) ->
	%%Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{user_id=user_id_extract(Status)},
	Status;
handle_nas({?MM_Auth_Response,Option,IEs},S1Ap_Nas,Status) ->
	Status;





handle_nas({?MM_TA_Update_Accept,Option,IEs},S1Ap_Nas,Status) ->
	Status;

handle_nas({?MM_TA_Update_Complete,Option,IEs},S1Ap_Nas,Status) ->
	Status;

%% Attached request

handle_nas({?MM_Attach_Accept,Option,IEs},S1Ap_Nas,Status) ->
	Status;
handle_nas({?MM_Attach_Complete,Option,IEs},S1Ap_Nas,Status) ->
	Status;
handle_nas({?MM_Attach_Reject,Option,IEs},S1Ap_Nas,Status) ->
	Status;
handle_nas({?MM_Detach_Request,Option,IEs},S1Ap_Nas,Status) ->
	Status;
handle_nas({?MM_Detach_Accept,Option,IEs},S1Ap_Nas,Status) ->
	Status;

handle_nas({?MM_EMM_Info,Option,IEs},S1Ap_Nas,Status) ->
	Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{rrc_con_reason=emm_info,user_id=user_id_extract(Status)},
	Status#sub_lte{status = emm_info,step = 1,cdr=Cdr};
handle_nas({?MM_EMM_State,Option,IEs},S1Ap_Nas,Status) ->
	Status;


handle_nas({Msg_Type,Option,IEs},S1Ap_Nas,Status) when is_record(S1Ap_Nas,s1ap_nas) ->
	io:format(" msg type with option ~p of index:~p~n",[Msg_Type,S1Ap_Nas#s1ap_nas.index]),
	%%Status#sub_lte{cdr=end_nas_session(Status#sub_lte.cdr,force),status = idle};
  Status;

handle_nas({Msg_Type,IEs},S1Ap_Nas,Status) ->
  io:format(" msg type w/o option ~p of index:~p~n",[Msg_Type,S1Ap_Nas#s1ap_nas.index]),
	Status;

handle_nas(Msg,S1Ap_Nas,Status) ->
  io:format(" wired msg of index:~p~n",[S1Ap_Nas#s1ap_nas.index]),
	Status.


nas_element(guti,<<Length:8,Payload/binary>>,Status) ->
	<<Element:Length/binary,Other/binary>> = Payload,
	<<_:5,Type:3,Content/binary>> = Element,
	NewStatus = case Type of
		1 ->
			Imsi = decode(imsi,Content),
			subscriber_sup:associate({imsi,Imsi},self(),[force]), %% todo error handle required
			Status#sub_lte{imsi = Imsi};
		6 ->
			Guti = decode(guti,Content),
      if
				Status#sub_lte.tmsi =:= undefined ->
					subscriber_sup:associate({tmsi,Guti#guti.tmsi},self(),[detect]); %% todo error handle required in fact, it equals to tmsi
				true ->
					ok
			end,
      %%Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{user_id = {tmsi,Guti#guti.tmsi}},
			Status#sub_lte{tmsi = Guti#guti.tmsi};
		3 ->
			Imei = decode(imei,Content),
			subscriber_sup:associate({imei,Imei},self(),[detect]), %% todo error handle required in fact, it equals to tmsi
			%%Cdr = (Status#sub_lte.cdr)#lte_s1_cdr{user_id = {imei,Imei}},
      Status#sub_lte{imei = Imei};
		_ ->
			Status
		end,
	{Other,NewStatus};
nas_element(_,Payload,Status) ->
	{Payload,Status}.


decode(imsi,Content) ->
  <<M1:4/integer,M0:4/integer,M3:4/integer,M2:4/integer,M5:4/integer,M4:4/integer,M7:4/integer,M6:4/integer,
    M9:4/integer,M8:4/integer,M11:4/integer,M10:4/integer,M13:4/integer,M12:4/integer>> = Content,
  {M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13};

decode(guti,Content) ->
	<<MCC2:4,MCC1:4,_Tag:4,MCC3:4,MNC2:4,MNC1:4,MME_G_ID:16/integer,MME_Code:8/integer,Tmsi:4/binary>> = Content,
	#guti{mcc = {MCC1,MCC2,MCC3},mnc = {MNC2,MNC1},mme = {MME_G_ID,MME_Code}, tmsi = binary:decode_unsigned((Tmsi),little)}.

user_id_extract(#sub_lte{imsi=undefined,guti=undefined,tmsi=undefined}) ->
	{unknown,unknown};

user_id_extract(#sub_lte{imsi=undefined,guti=undefined,tmsi=Tmsi}) ->
	{tmsi,Tmsi};

user_id_extract(#sub_lte{imsi=undefined,guti=Guti})  ->
	{guti,Guti#guti.tmsi};

user_id_extract(#sub_lte{imsi=Imsi}) ->
	{imsi,Imsi}.



end_nas_session(Cdr,force) ->
	gen_event:notify(cdr_event,{cdr,Cdr#lte_s1_cdr{end_mode=force,end_time=calendar:local_time()}}),
	#lte_s1_cdr{};
end_nas_session(Cdr,Other) ->
	gen_event:notify(cdr_event,{cdr,Cdr#lte_s1_cdr{end_mode=Other}}),
	#lte_s1_cdr{}.
